home *** CD-ROM | disk | FTP | other *** search
/ POINT Software Programming / PPROG1.ISO / pascal / swag / comm.swg / 0047_Complete Comm package.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1994-08-24  |  26.3 KB  |  728 lines

  1. { ---------------------------------------------------------------------------
  2.   unit COM.PAS
  3.  
  4.   Turbo Pascal (version 4.0 or higher) unit for serial communication which
  5.   is based on interrupt routines and includes buffering of incoming data.
  6.  
  7.   Features:
  8.  
  9.   - supports COM1 and COM2 in parallel
  10.   - baudrates up to 115200 baud
  11.   - RTS/CTS and XON/XOFF flow control
  12.  
  13.   Version 3.0 - May 1994
  14.  
  15.   Copyright 1994, Willem van Schaik - Oirschot - Netherlands
  16.  
  17.   ---------------------------------------------------------------------------
  18. }
  19.  
  20.   unit Com;
  21.  
  22.   interface
  23.  
  24.   uses Crt, Dos;
  25.  
  26.   type
  27.     PortType = (COM1, COM2);
  28.     BaudType = (B110, B150, B300, B600, B1200, B2400, B4800,
  29.        B9600, B19200, B38400, B57600, B115200);
  30.     ParityType = (None, Odd, Even, Mark, Space);
  31.     LengthType = (D5, D6, D7, D8);
  32.     StopType = (S1, S2);
  33.     FlowType = (No, RtsCts, XonXoff);
  34.  
  35.   procedure InitCom (PortNumber : PortType;
  36.          BaudRate : BaudType;
  37.                      ParityBit : ParityType;
  38.          DataLength : LengthType;
  39.          StopBits : StopType;
  40.          FlowControl : FlowType);
  41.   procedure ExitCom (PortNumber : PortType);
  42.   function  ComReceived (PortNumber : PortType) : boolean;
  43.   function  ReadCom (PortNumber : PortType) : char;
  44.   function  ComAllowed (PortNumber : PortType) : boolean;
  45.   procedure WriteCom (PortNumber : PortType; OutByte : char);
  46.   procedure BreakCom (PortNumber : PortType);
  47.  
  48.   implementation
  49.  
  50.   type
  51.     IntBlock = record
  52.       IntOldIP : integer;
  53.       IntOldCS : integer;
  54.       IntNumber : byte;
  55.     end;
  56.  
  57.     INS8250 = record
  58.       DLL : integer;  { divisor latch low register (if LCR bit7 = 1) }
  59.       DLH : integer;  { divisor latch high register (if LCR bit7 = 1) }
  60.       THR : integer;  { transmit holding register }
  61.       RBR : integer;  { receive holding register }
  62.       IER : integer;  { interrupt enable register }
  63.       LCR : integer;  { line control register }
  64.       MCR : integer;  { modem control register }
  65.       LSR : integer;  { line status register }
  66.       MSR : integer;  { modem status register }
  67.     end;
  68.  
  69.   const
  70.     IntDS : integer = 0;
  71.     ComPort : array [COM1..COM2] of INS8250 =
  72.       ((DLL : $3F8 ; DLH : $3F9 ; THR : $3F8 ; RBR : $3F8 ;
  73.         IER : $3F9 ; LCR : $3FB ; MCR : $3FC ; LSR : $3FD ; MSR : $3FE),
  74.        (DLL : $2F8 ; DLH : $2F9 ; THR : $2F8 ; RBR : $2F8 ;
  75.         IER : $2F9 ; LCR : $2FB ; MCR : $2FC ; LSR : $2FD ; MSR : $2FE));
  76.     { size of the input buffer and the amount of free space to disable flow
  77.       from the other side and to enable it again }
  78.     ComBufferSize = 4096;
  79.     ComFlowLower = 256;
  80.     ComFlowUpper = 1024;
  81.  
  82.   var
  83.     ComBuffer : array [COM1 .. COM2, 0..(ComBufferSize-1)] of byte;
  84.     ComBufferHead, ComBufferTail : array [COM1 .. COM2] of integer;
  85.     ComFlowControl : array [COM1 .. COM2] of FlowType;
  86.     ComFlowHalted : array [COM1 .. COM2] of boolean;
  87.     ComXoffReceived : array [COM1 .. COM2] of boolean;
  88.     ComBlock : array [COM1 .. COM2] of IntBlock;
  89.  
  90. { ---------------------------------------------------------------------------
  91.   InstallComInt
  92.  
  93.   To install an interrupt routine, first the old routine vector is read and
  94.   stored using function 35 hex. Next the new routine is installed using
  95.   function 25 hex.
  96.   ---------------------------------------------------------------------------
  97. }
  98.  
  99.   procedure InstallComInt (IntNumber : byte; IntHandler : integer;
  100.                            var Block : IntBlock);
  101.   var
  102.     Regs : Registers;
  103.  
  104.   begin
  105.     IntDS := DSeg;
  106.     Block.IntNumber := IntNumber;
  107.     Regs.AH := $35;
  108.     Regs.AL := IntNumber;
  109.     MSDos (Dos.Registers(Regs));
  110.     Block.IntOldCS := Regs.ES;
  111.     Block.IntOldIP := Regs.BX;
  112.     Regs.AH := $25;
  113.     Regs.AL := IntNumber;
  114.     Regs.DS := CSeg;
  115.     Regs.DX := IntHandler;
  116.     MSDos (Dos.Registers(Regs));
  117.   end;
  118.  
  119. { ---------------------------------------------------------------------------
  120.   UnInstallComInt
  121.  
  122.   Uninstalling the interrupt routine is done by resetting the old interrupt
  123.   vector using function 25.
  124.   ---------------------------------------------------------------------------
  125. }
  126.  
  127.   procedure UnInstallComInt (var Block : IntBlock);
  128.  
  129.   var
  130.     Regs : Registers;
  131.  
  132.   begin
  133.     Regs.AH := $25;
  134.     Regs.AL := Block.IntNumber;
  135.     Regs.DS := Block.IntOldCS;
  136.     Regs.DX := Block.IntOldIP;
  137.     MSDos (Dos.Registers(Regs));
  138.   end;
  139.  
  140. { ---------------------------------------------------------------------------
  141.   Com1IntHandler
  142.  
  143.   This routine is installed as the interrupt routine by InstallComInt, which
  144.   in its turn is called by InitCom at initialisation of the unit.
  145.  
  146.   When a byte arrives at the COM-port, first action is to get the byte from
  147.   the UART register and store it the buffer. Next the buffer pointer is
  148.   increased. Depending on flow control being enabled or not, it is checked if
  149.   the free space has become less then ComFlowLower and if that is the case the
  150.   other party (the DCE) is signalled to stop transmitting data.
  151.  
  152.   When the type of flow control specified at calling InitCom is RtsCts (this
  153.   is hardware flow control), the RTS bit of the MCR register is lowered. If
  154.   flow control is XonXoff (software flow control), an XOFF character (13 hex)
  155.   is send to the other party by calling WriteCom.
  156.  
  157.   Finally the routine must be ended with a CLI instruction and the interrupt
  158.   flags must be cleared.
  159.   ---------------------------------------------------------------------------
  160. }
  161.  
  162.   procedure Com1IntHandler (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP
  163. : word);
  164.   interrupt;
  165.  
  166.   begin
  167.     ComBuffer[COM1, ComBufferHead[COM1]] := Port[ComPort[COM1].RBR];
  168.     if ComFlowControl[COM1] = No then
  169.     begin
  170.       ComBufferHead[COM1] := (ComBufferHead[COM1] + 1) mod ComBufferSize;
  171.     end
  172.     else { when flow control increase buffer pointer later }
  173.     begin
  174.       { check for incoming XON/XOFF }
  175.       if ComFlowControl[COM1] = XonXoff then
  176.       begin
  177.         if ComBuffer[COM1, ComBufferHead[COM1]] = $11 then { XON }
  178.           ComXoffReceived[COM1] := false
  179.         else if ComBuffer[COM1, ComBufferHead[COM1]] = $13 then { XOFF }
  180.           ComXoffReceived[COM1] := true;
  181.       end;
  182.       ComBufferHead[COM1] := (ComBufferHead[COM1] + 1) mod ComBufferSize;
  183.       { check if outgoing must be temporized }
  184.       if not ComFlowHalted[COM1] then
  185.         if (ComBufferHead[COM1] >= ComBufferTail[COM1]) and
  186.    (ComBufferTail[COM1] - ComBufferHead[COM1] + ComBufferSize < ComFlowLower)
  187.    or
  188.    (ComBufferHead[COM1] < ComBufferTail[COM1]) and
  189.    (ComBufferTail[COM1] - ComBufferHead[COM1] < ComFlowLower) then
  190.         begin { buffer gets too full }
  191.    if ComFlowControl[COM1] = RtsCts then
  192.      Port[ComPort[COM1].MCR] := Port[ComPort[COM1].MCR] and $FD { lower RTS }
  193.    else if ComFlowControl[COM1] = XonXoff then
  194.      WriteCom (COM1, #$13); { send XOFF }
  195.    ComFlowHalted[COM1] := true;
  196.         end;
  197.     end;
  198.     inline ($FA);                         { CLI }
  199.     Port[$20] := $20;                     { clear interrupt flag }
  200.   end;
  201.  
  202. { ---------------------------------------------------------------------------
  203.   Com2IntHandler
  204.  
  205.   This routine is identical to Com1IntHandler, only for COM2.
  206.   ---------------------------------------------------------------------------
  207. }
  208.  
  209.   procedure Com2IntHandler (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP : word);
  210.   interrupt;
  211.  
  212.   begin
  213.     ComBuffer[COM2, ComBufferHead[COM2]] := Port[ComPort[COM2].RBR];
  214.     if ComFlowControl[COM2] = No then
  215.     begin
  216.       ComBufferHead[COM2] := (ComBufferHead[COM2] + 1) mod ComBufferSize;
  217.     end
  218.     else { when flow control increase buffer pointer later }
  219.     begin
  220.       { check for incoming XON/XOFF }
  221.       if ComFlowControl[COM2] = XonXoff then
  222.       begin
  223.         if ComBuffer[COM2, ComBufferHead[COM2]] = $11 then { XON }
  224.           ComXoffReceived[COM2] := false
  225.         else if ComBuffer[COM2, ComBufferHead[COM2]] = $13 then { XOFF }
  226.           ComXoffReceived[COM2] := true;
  227.       end;
  228.       ComBufferHead[COM2] := (ComBufferHead[COM2] + 1) mod ComBufferSize;
  229.       { check if outgoing must be temporized }
  230.       if not ComFlowHalted[COM2] then
  231.         if (ComBufferHead[COM2] >= ComBufferTail[COM2]) and
  232.    (ComBufferTail[COM2] - ComBufferHead[COM2] + ComBufferSize < ComFlowLower)
  233.    or
  234.    (ComBufferHead[COM2] < ComBufferTail[COM2]) and
  235.    (ComBufferTail[COM2] - ComBufferHead[COM2] < ComFlowLower) then
  236.         begin { buffer gets too full }
  237.    if ComFlowControl[COM2] = RtsCts then
  238.      Port[ComPort[COM2].MCR] := Port[ComPort[COM2].MCR] and $FD { lower RTS }
  239.    else if ComFlowControl[COM2] = XonXoff then
  240.      WriteCom (COM2, #$13); { send XOFF }
  241.    ComFlowHalted[COM2] := true;
  242.         end;
  243.     end;
  244.     inline ($FA);                         { CLI }
  245.     Port[$20] := $20;                     { clear interrupt flag }
  246.   end;
  247.  
  248. { ---------------------------------------------------------------------------
  249.   InitCom;
  250.  
  251.   For each of the COM ports that will be used, this routine must be called
  252.   to initialize the UART and to install the interrrupt routine. The first
  253.   five parameters define the serial protocol (baudrate B150..B11500, parity
  254.   None..Space, length D5..D8 and number of stop bits S1 or S2). The last
  255.   parameter specifies the type of flow control, with allowed values No,
  256.   RtsCts and XonXoff.
  257.  
  258.   The control signals DTR and RTS of the COM port (plus the OUT2 signal, which
  259.   is used by some internal modems) are raised to signal the other end of the
  260.   line that the port is ready to receive data.
  261.   ---------------------------------------------------------------------------
  262. }
  263.  
  264.   procedure InitCom; { (PortNumber : PortType;
  265.             BaudRate : BaudType;
  266.                         ParityBit : ParityType;
  267.             DataLength : LengthType;
  268.             StopBits : StopType;
  269.             FlowControl : FlowType); }
  270.   const
  271.     BaudReg : array [B110 .. B115200] of word =
  272.       ($0417, $0300, $0180, $00C0, $0060, $0030,
  273.        $0018, $000C, $0006, $0003, $0002, $0001);
  274.     ParityReg : array [None..Space] of byte =
  275.       ($00, $08, $18, $28, $38);
  276.     LengthReg : array [D5 .. D8] of byte =
  277.       ($00, $01, $02, $03);
  278.     StopReg : array [S1 .. S2] of byte =
  279.       ($00, $04);
  280.  
  281.   var
  282.     Regs : Registers;
  283.  
  284.   begin
  285.     { enable the interrupt (IRQ4 resp. IRQ3) for the specified COM port, by
  286.       resetting the bits in the Interrupt Mask Register of the 8259 interrupt
  287.       controller }
  288.     if PortNumber = COM1 then
  289.     begin
  290.       InstallComInt($0C, Ofs(Com1IntHandler), ComBlock[COM1]);
  291.       Port[$21] := Port[$21] and $EF
  292.     end
  293.     else if PortNumber = COM2 then
  294.     begin
  295.       InstallComInt($0B, Ofs(Com2IntHandler), ComBlock[COM2]);
  296.       Port[$21] := Port[$21] and $F7
  297.     end;
  298.  
  299.     Port[ComPort[PortNumber].LCR] := $80; { switch to write latch reg }
  300.     Port[ComPort[PortNumber].DLH] := Hi (BaudReg [BaudRate]);
  301.     Port[ComPort[PortNumber].DLL] := Lo (BaudReg [BaudRate]);
  302.     Port[ComPort[PortNumber].LCR] := $00 or
  303.          ParityReg [ParityBit] or
  304.          LengthReg [DataLength] or
  305.          StopReg [StopBits];
  306.     Port[ComPort[PortNumber].IER] := $01; { enable interrupts }
  307.     Port[ComPort[PortNumber].MCR] := $01 or { raise DTR }
  308.                 $02 or { raise RTS }
  309.          $08;   { raise OUT2 }
  310.     ComBufferHead[PortNumber] := 0;
  311.     ComBufferTail[PortNumber] := 0;
  312.     ComFlowControl[PortNumber] := FlowControl;
  313.     ComFlowHalted[PortNumber] := false;
  314.     ComXoffReceived[PortNumber] := false;
  315.   end;
  316.  
  317. { ---------------------------------------------------------------------------
  318.   ExitCom;
  319.  
  320.   This routine must be called for each COM port in use, to remove the
  321.   interrupt routine and to reset the control lines.
  322.   ---------------------------------------------------------------------------
  323. }
  324.  
  325.   procedure ExitCom; { (PortNumber : PortType) }
  326.  
  327.   var
  328.     Regs : Registers;
  329.  
  330.   begin
  331.     { disable the interrupt (IRQ4 resp. IRQ3) for the specified COM port, by
  332.       setting the bits in the Interrupt Mask Register of the 8259 interrupt
  333.       controller }
  334.     if PortNumber = COM1 then
  335.       Port[$21] := Port[$21] or $10
  336.     else if PortNumber = COM2 then
  337.       Port[$21] := Port[$21] or $08;
  338.  
  339.     Port[ComPort[PortNumber].LCR] := Port[ComPort[PortNumber].LCR] and $7F;
  340.     Port[ComPort[PortNumber].IER] := 0; { disable interrupts }
  341.     Port[ComPort[PortNumber].MCR] := 0; { lower DTR, RTS and OUT2 }
  342.     UnInstallComInt(ComBlock[PortNumber]);
  343.   end;
  344.  
  345. { ---------------------------------------------------------------------------
  346.   ComReceived;
  347.  
  348.   When the head and tail pointer (for writing resp. reading bytes) are not
  349.   pointing to the same byte in the buffer, a byte has arrived from the UART
  350.   and was stored in the buffer by the interrupt routine.
  351.   ---------------------------------------------------------------------------
  352. }
  353.  
  354.   function ComReceived; { (PortNumber : PortType) : boolean; }
  355.  
  356.   begin
  357.     ComReceived := ComBufferHead[PortNumber] <> ComBufferTail[PortNumber];
  358.   end;
  359.  
  360. { ---------------------------------------------------------------------------
  361.   ReadCom;
  362.  
  363.   Calling this function will wait for a byte in the buffer (if there is not
  364.   yet one present) and then return it. The tail buffer pointer is increased
  365.   and if flow from the other side was stopped, a check is made if the free
  366.   space has again become more then ComFlowUpper. In that situation, depending
  367.   on the type of flow control, either the RTS line is raised or and XON byte
  368.   (11 hex) is send to the other party.
  369.   ---------------------------------------------------------------------------
  370. }
  371.  
  372.   function ReadCom; { (PortNumber : PortType) : char; }
  373.  
  374.   begin
  375.     while ComBufferHead[PortNumber] = ComBufferTail[PortNumber] do Delay(10);
  376.     ReadCom := char(ComBuffer[PortNumber, ComBufferTail[PortNumber]]);
  377.     ComBufferTail[PortNumber] := (ComBufferTail[PortNumber] + 1) mod ComBufferSize;
  378.     if (ComFlowControl[PortNumber] <> No) and ComFlowHalted[PortNumber] then
  379.       if (ComBufferHead[PortNumber] >= ComBufferTail[PortNumber]) and
  380.         (ComBufferTail[PortNumber] - ComBufferHead[PortNumber] + ComBufferSize > ComFlowUpper) or
  381.         (ComBufferHead[PortNumber] < ComBufferTail[PortNumber]) and
  382.         (ComBufferTail[PortNumber] - ComBufferHead[PortNumber] > ComFlowUpper)
  383. then
  384.       begin { buffer has emptied enough }
  385.         if ComFlowControl[PortNumber] = RtsCts then
  386.    Port[ComPort[PortNumber].MCR] := Port[ComPort[PortNumber].MCR] or $02 {
  387. raise RTS }
  388.         else if ComFlowControl[PortNumber] = XonXoff then
  389.    WriteCom (PortNumber, #$11); { send XON }
  390.         ComFlowHalted[PortNumber] := false;
  391.       end;
  392.   end;
  393.  
  394. { ---------------------------------------------------------------------------
  395.   ComAllowed;
  396.  
  397.   With this function it is possible to check if writing data to the COM port
  398.   is allowed. When there is no flow control no check is made on any control
  399.   line and the result will always be true. When hardware type flow control is
  400.   enabled, DSR (and CD) and CTS must be high. In case of software flow
  401.   control DSR must be high and a check is made if an XOFF byte was received.
  402.   ---------------------------------------------------------------------------
  403. }
  404.  
  405.   function  ComAllowed; { (PortNumber : PortType) : boolean; }
  406.  
  407.   begin
  408.     ComAllowed := true;
  409.     if (ComFlowControl[PortNumber] = RtsCts) then
  410.     begin
  411.       { replace in next line both $30 with $B0 for checking on CD, DSR and CTS}
  412.       if ((Port[ComPort[PortNumber].MSR] and $30) <> $30) then { no DSR or CTS}
  413.         ComAllowed := false;
  414.     end
  415.     else if (ComFlowControl[PortNumber] = XonXoff) then
  416.     begin
  417.       { replace in next line both $20 with $A0 for checking on CD and DSR }
  418.       if ((Port[ComPort[PortNumber].MSR] and $20) <> $20) or { no DSR }
  419.          (ComXoffReceived[PortNumber]) then { XOFF received }
  420.         ComAllowed := false;
  421.     end
  422.   end;
  423.  
  424. { ---------------------------------------------------------------------------
  425.   WriteCom;
  426.  
  427.   This routine is to write a byte to the COM port. However, when necessary
  428.   this will be delayed until the previous output byte is out the the UART.
  429.   ---------------------------------------------------------------------------
  430. }
  431.  
  432.   procedure WriteCom; { (PortNumber : PortType; OutByte : char); }
  433.  
  434.   begin
  435.     while ((Port[ComPort[PortNumber].LSR] and $20) <> $20) do  { TD empty }
  436.       Delay(1);
  437.     Port[ComPort[PortNumber].THR] := byte(OutByte);
  438.   end;
  439.  
  440. { ---------------------------------------------------------------------------
  441.   BreakCom;
  442.  
  443.   With this routine the TD line can be lowered for 200 msec, which is a so-
  444.   called break signal.
  445.   ---------------------------------------------------------------------------
  446. }
  447.  
  448.   procedure BreakCom; { (PortNumber : PortType); }
  449.  
  450.   begin
  451.     Port[ComPort[PortNumber].LCR] := Port[ComPort[PortNumber].LCR] or $40;
  452.     Delay (200);  { 0.2 seconds }
  453.     Port[ComPort[PortNumber].LCR] := Port[ComPort[PortNumber].LCR] and $BF;
  454.   end;
  455.  
  456.   end.
  457.  
  458. { ---------------------------------------------------------------------------
  459.   end of COM.PAS
  460.   ---------------------------------------------------------------------------
  461. }
  462.  
  463. { ---------------------------------------------------------------------------
  464.   program TTY.PAS
  465.  
  466.   Sample terminal emulation using simple teletype protocol to be used with
  467.   the unit COM.PAS for serial communnication.
  468.  
  469.   Features:
  470.  
  471.   - switching between COM1 and COM2
  472.   - baudrates up to 115200 baud
  473.   - RTS/CTS and XON/XOFF flow control
  474.   - debug mode to display control characters
  475.  
  476.   Version 3.0 - May 1994
  477.  
  478.   Copyright 1994, Willem van Schaik - Oirschot - Netherlands
  479.  
  480.   ---------------------------------------------------------------------------
  481. }
  482.  
  483.   program Tty;
  484.  
  485.   uses Crt, Com;
  486.  
  487.   const
  488.     Ascii : array [0..255] of string [5] =
  489.       ('<NUL>','<SOH>','<STX>','<ETX>','<EOT>','<ENQ>','<ACK>','<BEL>',
  490.        '<BS>','<HT>','<LF>','<VT>','<FF>','<CR>','<SO>','<SI>',
  491.        '<DLE>','<DC1>','<DC2>','<DC3>','<DC4>','<NAK>','<SYN>','<ETB>',
  492.        '<CAN>','<EM>','<SUB>','<ESC>','<FS>','<GS>','<RS>','<US>',
  493.        ' ','!','"','#','$','%','&','''','(',')','*','+',',','-','.','/',
  494.        '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
  495.        '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
  496.        'P','Q','R','S','T','U','V','W','X','Y','Z','[','\',']','^','_',
  497.        '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
  498.        'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','<DEL>',
  499.        '<128>','<129>','<130>','<131>','<132>','<133>','<134>','<135>',
  500.        '<136>','<137>','<138>','<139>','<140>','<141>','<142>','<143>',
  501.        '<144>','<145>','<146>','<147>','<148>','<149>','<150>','<151>',
  502.        '<152>','<153>','<154>','<155>','<156>','<157>','<158>','<159>',
  503.        '<160>','<161>','<162>','<163>','<164>','<165>','<166>','<167>',
  504.        '<168>','<169>','<170>','<171>','<172>','<173>','<174>','<175>',
  505.        '<176>','<177>','<178>','<179>','<180>','<181>','<182>','<183>',
  506.        '<184>','<185>','<186>','<187>','<188>','<189>','<190>','<191>',
  507.        '<192>','<193>','<194>','<195>','<196>','<197>','<198>','<199>',
  508.        '<200>','<201>','<202>','<203>','<204>','<205>','<206>','<207>',
  509.        '<208>','<209>','<210>','<211>','<212>','<213>','<214>','<215>',
  510.        '<216>','<217>','<218>','<219>','<220>','<221>','<222>','<223>',
  511.        '<224>','<225>','<226>','<227>','<228>','<229>','<230>','<231>',
  512.        '<232>','<233>','<234>','<235>','<236>','<237>','<238>','<239>',
  513.        '<240>','<241>','<242>','<243>','<244>','<245>','<246>','<247>',
  514.        '<248>','<249>','<250>','<251>','<252>','<253>','<254>','<255>');
  515.  
  516.   var
  517.     TtyPort : PortType;
  518.     TtyBaud : BaudType;
  519.     TtyParity : ParityType;
  520.     TtyLength : LengthType;
  521.     TtyStop : StopType;
  522.     TtyFlow : FlowType;
  523.  
  524.     ChCom, ChKey : char;
  525.     DoDebug : boolean;
  526.     GoExit : boolean;
  527.  
  528. { ---------------------------------------------------------------------------
  529.   TtyGetPars
  530.  
  531.   Procedure to handle alt-key combinations that are used to change the
  532.   settings of the terminal emulation protocol.
  533.   ---------------------------------------------------------------------------
  534. }
  535.  
  536.   procedure TtyGetPars (AltKey : char);
  537.  
  538.   var
  539.     ParsInput : string[16];
  540.  
  541.   begin
  542.     case AltKey of
  543.  
  544.       #120:  { alt-1 }
  545.       begin
  546.         if WhereX > 1 then Writeln;
  547.         Writeln ('TTY:  port = COM1:');
  548.         if TtyPort <> COM1 then
  549.         begin
  550.           ExitCom (TtyPort);
  551.           TtyPort := COM1;
  552.           InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow)
  553.         end;
  554.       end;
  555.  
  556.       #121:  { alt-2 }
  557.       begin
  558.         if WhereX > 1 then Writeln;
  559.         Writeln ('TTY:  port = COM2:');
  560.         if TtyPort <> COM2 then
  561.         begin
  562.           ExitCom (TtyPort);
  563.           TtyPort := COM2;
  564.           InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow)
  565.         end;
  566.       end;
  567.  
  568.       #48:  { alt-B }
  569.       begin
  570.         if WhereX > 1 then Writeln;
  571.         Write ('TTY:  baudrate = ');
  572.         Readln (ParsInput);
  573.         if (ParsInput = '3') or (ParsInput = '300') then TtyBaud := B300
  574.         else if (ParsInput = '6') or (ParsInput = '600') then TtyBaud := B600
  575.         else if (ParsInput = '12') or (ParsInput = '1200') then TtyBaud := B1200
  576.         else if (ParsInput = '24') or (ParsInput = '2400') then TtyBaud := B2400
  577.         else if (ParsInput = '48') or (ParsInput = '4800') then TtyBaud := B4800
  578.         else if (ParsInput = '96') or (ParsInput = '9600') then TtyBaud := B9600
  579.         else if (ParsInput = '192') or (ParsInput = '19200') then TtyBaud := B19200
  580.         else if (ParsInput = '384') or (ParsInput = '38400') then TtyBaud := B38400
  581.         else
  582.           Writeln ('      baudrate = 300,600,1200,2400,4800,9600,19200,38400');
  583.         ExitCom (TtyPort);
  584.         InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow);
  585.       end;
  586.  
  587.       #38:  { alt-L }
  588.       begin
  589.         if WhereX > 1 then Writeln;
  590.         Write ('TTY:  word length = ');
  591.         Readln (ParsInput);
  592.         case ParsInput[1] of
  593.           '5': TtyLength := D5;
  594.           '6': TtyLength := D6;
  595.           '7': TtyLength := D7;
  596.           '8': TtyLength := D8;
  597.         else
  598.           Writeln ('      word length = 5,6,7,8');
  599.         end;
  600.         ExitCom (TtyPort);
  601.         InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow);
  602.       end;
  603.  
  604.       #25:  { alt-P }
  605.       begin
  606.         if WhereX > 1 then Writeln;
  607.         Write ('TTY:  parity bit = ');
  608.         Readln (ParsInput);
  609.         case ParsInput[1] of
  610.           'n', 'N': TtyParity := None;
  611.           'o', 'O': TtyParity := Odd;
  612.           'e', 'E': TtyParity := Even;
  613.           'm', 'O': TtyParity := Mark;
  614.           's', 'O': TtyParity := Space;
  615.         else
  616.           Writeln ('      parity bit = none,odd,even,mark,space');
  617.         end;
  618.         ExitCom (TtyPort);
  619.         InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow);
  620.       end;
  621.  
  622.       #31:  { alt-S }
  623.       begin
  624.         if WhereX > 1 then Writeln;
  625.         Write ('TTY:  stop bits = ');
  626.         Readln (ParsInput);
  627.         case ParsInput[1] of
  628.           '1': TtyStop := S1;
  629.           '2': TtyStop := S2;
  630.         else
  631.           Writeln ('      stop bits = 1,2');
  632.         end;
  633.         ExitCom (TtyPort);
  634.         InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow);
  635.       end;
  636.  
  637.       #33:  { alt-F }
  638.       begin
  639.         if WhereX > 1 then Writeln;
  640.         Write ('TTY:  flow control = ');
  641.         Readln (ParsInput);
  642.         case ParsInput[1] of
  643.           'n', 'N': TtyFlow := No;
  644.           'r', 'R': TtyFlow := RtsCts;
  645.           'x', 'X': TtyFlow := XonXoff;
  646.         else
  647.           Writeln ('      flow control = no,rts/cts,xon/xoff');
  648.         end;
  649.         ExitCom (TtyPort);
  650.         InitCom (TtyPort, TtyBaud, TtyParity, TtyLength, TtyStop, TtyFlow);
  651.       end;
  652.  
  653.       #23:  { alt-I }
  654.       begin
  655.         if WhereX > 1 then Writeln;
  656.         Write ('TTY:  port = COM', ord(TtyPort)+1, ':      ');
  657.         case TtyBaud of
  658.           B110: Write ('baudrate = 110          ');
  659.           B150: Write ('baudrate = 150          ');
  660.           B300: Write ('baudrate = 300          ');
  661.           B600: Write ('baudrate = 600          ');
  662.           B1200: Write ('baudrate = 1200         ');
  663.           B2400: Write ('baudrate = 2400         ');
  664.           B4800: Write ('baudrate = 4800         ');
  665.           B9600: Write ('baudrate = 9600         ');
  666.           B19200: Write ('baudrate = 19200        ');
  667.           B38400: Write ('baudrate = 38400        ');
  668.           B57600: Write ('baudrate = 57600        ');
  669.           B115200: Write ('baudrate = 115200       ');
  670.         end;
  671.         case TtyParity of
  672.           None: Writeln ('parity bit = none');
  673.           Odd: Writeln ('parity bit = odd');
  674.           Even: Writeln ('parity bit = even');
  675.           Mark: Writeln ('parity bit = mark');
  676.           Space: Writeln ('parity bit = space');
  677.         end;
  678.         case TtyFlow of
  679.           No: Write ('      flow = no         ');
  680.           RtsCts: Write ('      flow = rts/cts    ');
  681.           XonXoff: Write ('      flow = xon/xoff   ');
  682.         end;
  683.         Write ('word length = ', ord(TtyLength)+5, '         ');
  684.         Writeln ('stop bits = ', ord(TtyStop)+1);
  685.       end;
  686.  
  687.       #35:  { alt-H }
  688.       begin
  689.         if WhereX > 1 then Writeln;
  690.         Write ('TTY:  alt-1 - COM1      ');
  691.         Write ('alt-B - baudrate        ');
  692.         Write ('alt-I - info');
  693.         Writeln;
  694.         Write ('      alt-2 - COM2      ');
  695.         Write ('alt-L - word length     ');
  696.         Write ('alt-H - help');
  697.         Writeln;
  698.         Write ('      alt-F - flow      ');
  699.         Write ('alt-P - parity bit');
  700.         Writeln;
  701.         Write ('      alt-D - debug     ');
  702.         Write ('alt-S - stop bits       ');
  703.         Write ('alt-X - exit');
  704.         Writeln;
  705.       end;
  706.  
  707.       #32:  { alt-D }
  708.       begin
  709.         DoDebug := not DoDebug;
  710.         if WhereX > 1 then Writeln;
  711.         if DoDebug then
  712.           Writeln ('TTY:  debug = on')
  713.         else
  714.           Writeln ('TTY:  debug = off');
  715.       end;
  716.  
  717.       #45:  { alt-X }
  718.       begin
  719.         if WhereX > 1 then Writeln;
  720.         Writeln ('TTY:  exit');
  721.         GoExit := true;
  722.       end;
  723.  
  724.     end;  { case ChKey }
  725.   end;  { procedure TtyGetPars }
  726.  
  727. { ---------------------------------------------------------------------------
  728.